/*
 * Miccsnookersim, a snooker simulation with AI
 * Copyright (C) 2009 Enno Ruijters, Frans van den Heuvel, Yannick Thimister,
 * Ali B. Ozmen, Esther Verhoef, Achim Leydecker
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.awt.*;
import java.awt.event.*;
import java.awt.Color;

public class Cue implements MouseListener, MouseMotionListener
{
	private Vector force;	// Amount of force behind the push
	private Vector position; // Position on the table of the tip of the Cue
	private Table table;
	private Ball cueBall;
	private Frame frame;
	private double[] spinPos; // the spin in de z axis that is set seperately

	/* few vars needed for the mouse listening */
	public boolean mousePressedAtSpin;
	public double spinYPoint;
	public double spinXPoint;
	public boolean mouseAtSpin;

	/**
	 * Creates a new cue that listens on a given frame, and responds
	 * by shooting the cue ball on the given table.
	 */
	public Cue(Table t, Frame f)
	{
		Ball[] balls = t.getBalls();
		force = null;
		position = null;
		table = t;
		frame = f;
		spinPos = new double[2];
		mousePressedAtSpin = false;
		mouseAtSpin = false;
		spinYPoint = 0;
		spinXPoint = 0;

		for (Ball i : balls) {
			if (i.getColor() == Color.WHITE) {
				cueBall = i;
				break;
			}
		}
	}

	/**
	  * Creates a new cue not bound to any table
	  */
	public Cue()
	{
		force = null;
		position = null;
	}

	public Cue clone()
	{
		Cue ret;
		if (table != null && frame != null)
			ret = new Cue(table, frame);
		else
			ret = new Cue();
		ret.force = force.clone();
		ret.position = position.clone();
		return ret;
	}

	public void mousePressed(MouseEvent event)
	{
		mousePressedAtSpin = false;
		if (table.areBallsMoving())
			return;
		if (mouseAtSpin) 
		{ // is it pressed for spin?
			mousePressedAtSpin = true;
			spinYPoint =  event.getY() - Display.yOffset;
			spinXPoint = event.getX() - Display.xOffset;

			//Now for the calculations for the y axis, from a point on the screen to a real position
			spinPos[1] = spinYPoint;
			spinPos[1] -= 10;
			spinPos[1] /= Display.PIXELS_PER_CM;
			spinPos[1] /= Display.AIMSIZE;
			spinPos[1] -= cueBall.getRadius();
			if (spinPos[1] > cueBall.getRadius()
			        - cueBall.getRadius()/5
			        || spinPos[1] < -cueBall.getRadius()
			        + cueBall.getRadius()/5) {
				spinPos[1] = 0;
				spinYPoint = 0;
			}
			System.out.println(" spinPos[1] set at: " + spinPos[1]);
			// calculations for the x axis
			spinPos[0] = spinXPoint;
			spinPos[0] -= ((Table.getTableWidth() * Display.PIXELS_PER_CM) + 20) ;
			spinPos[0] /= Display.PIXELS_PER_CM;
			spinPos[0] /= Display.AIMSIZE;
			spinPos[0] -= cueBall.getRadius();
			if (spinPos[0] > cueBall.getRadius()
			        - cueBall.getRadius()/5
			        || spinPos[1] < -cueBall.getRadius()
			        + cueBall.getRadius()/5) {
				spinPos[0] = 0;
				spinYPoint = 0;
			}
			System.out.println(" spinPos[0] set at: " + spinPos[0]);
			/* it is not sure if the position has been made
			 * already or not so we use a seperate variable */
			frame.repaint();
		} 
		else {
			if (cueBall.getPosition().magnitude() == 1.0/0.0)
				return;
			Vector tmp;
			tmp = new Vector((event.getX() - Display.xOffset) / Display.PIXELS_PER_CM,
			                 (event.getY() - Display.yOffset) / Display.PIXELS_PER_CM,
					 0);
			tmp = tmp.sub_(cueBall.getPosition());
			tmp.scaleTo_(cueBall.getRadius());
			tmp = tmp.rotate_(Math.asin(-spinPos[0] / Ball.RADIUS));
			tmp = tmp.add_(cueBall.getPosition());
			position = tmp;
			position.add_(new Vector(0,0,spinPos[1]));
			mouseMoved(event);
		}
	}

	public void mouseReleased(MouseEvent event)
	{
		if (position == null)
			return;
		if (!mousePressedAtSpin
		    && cueBall.getPosition().magnitude() != 1.0/0.0) {
			synchronized (this) {
				this.notify();
			}
		}
		frame.repaint();
	}

	public void reset()
	{
		position = null;
		force = null;
	}

	public void mouseDragged(MouseEvent event)
	{
		if (!mousePressedAtSpin)
			mouseMoved(event);
		else
			mousePressed(event);
	}

	public void mouseMoved(MouseEvent event)
	{
		double x = event.getX();
		double y = event.getY();

		if (x > Display.xAndYSpin[0]
		    && x < (Display.xAndYSpin[0] + Display.bigBallWidth)
		    && y > Display.xAndYSpin[1]
		    && y < (Display.xAndYSpin[1] + Display.bigBallWidth)) {
			// pressed for spin
			mouseAtSpin = true;
		} else {
			if (cueBall.getPosition().magnitude() == 1.0/0.0)
				return;
			Vector newPoint, tmp;
			tmp = new Vector((event.getX() - Display.xOffset) / Display.PIXELS_PER_CM,
			                 (event.getY() - Display.yOffset) / Display.PIXELS_PER_CM,
					 0);
			newPoint = tmp.clone();
			tmp = tmp.sub_(cueBall.getPosition());
			tmp.scaleTo_(cueBall.getRadius());
			tmp = tmp.rotate_(Math.asin(-spinPos[0] / Ball.RADIUS));
			tmp = tmp.add_(cueBall.getPosition());
			tmp.add_(new Vector(0,0,spinPos[1]));
			position = tmp;
			if (cueBall.getPosition().magnitude() < 1.0/0.0)
				force = newPoint.sub_(cueBall.getPosition());
			mouseAtSpin = false;
		}
		frame.repaint();
	}
	public void mouseEntered(MouseEvent event) { }
	public void mouseExited(MouseEvent event) { }
	public void mouseClicked(MouseEvent event) { }


	/**
	 * Returns the Force of the Cue when it hits the ball
	 * @return the force in.......
	 */
	public Vector getForce()
	{
		return force;
	}

	/**
	 * 	Sets the amount of force you use to hit the white-ball
	 * 	@param newForce The amount of force (in....)
	 */
	public void setForce(Vector newForce)
	{
		force = newForce;
	}

	/**
	 * 	Returns the position of the Cue relative of the white-ball
	 * 	@return the Position as a Vector
	 */
	public Vector getPosition()
	{
		return position;
	}

	/**
	 * 	Sets the Position of the Cue relative of the white-ball
	 * 	@param newAngle the position of the cue relative to the Table
	 */
	public void setPosition(Vector pos)
	{
		this.position = pos;
	}
}
